package com.wuyan.web.upload.api;

import com.wuyan.helper.kit.DateHelper;
import com.wuyan.helper.kit.FileHelper;
import com.wuyan.helper.kit.FileTypeHelper;
import com.wuyan.helper.kit.RandomHelper;
import com.wuyan.web.base.helper.PubConfigHelper;
import com.wuyan.web.base.helper.rep.RepBody;
import com.wuyan.web.base.helper.rep.RepCodeEnum;
import com.wuyan.web.base.helper.rep.RepHelper;
import com.wuyan.web.base.helper.rep.RepPageData;
import com.wuyan.web.base.helper.req.CustomQueryHelper;
import com.wuyan.web.base.helper.req.CustomQueryParams;
import com.wuyan.web.upload.entity.PubConfig;
import com.wuyan.web.upload.entity.PubFile;
import com.wuyan.web.upload.form.PubFileForm;
import com.wuyan.web.upload.service.PubConfigService;
import com.wuyan.web.upload.service.PubFileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedOutputStream;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 文件上传
 *
 * @author wuyan
 */

@Slf4j
@RestController
@RequestMapping("/api")
public class UploadApi implements RepHelper {

    private static final String CONFIG_TYPE_SYSTEM_NAME = "system";
    private static final String CONFIG_KEY_FILE_SERVER_BASE_NAME = "file-server-base";
    private static final String CONFIG_KEY_ALLOW_FILE_TYPE_NAME = "allow-file-type";
    private static final String CONFIG_KEY_FILE_SAVE_PATH_NAME = "file-save-path";

    @Autowired
    private PubFileService pubFileService;

    @Autowired
    private PubConfigService pubConfigService;

    @Value("${wuyan.save-path}")
    private String defaultSavePathRoot;

    @Value("${wuyan.server-base}")
    private String defaultFileServerBase;

    @Value("${wuyan.allow-file-type}")
    private String defaultAllowFileType;

    /**
     * 文件上传：同时支持多文件
     *
     * @return RepBody<PubFile>
     */
    @PostMapping(value = "/upload")
    public RepBody<List<PubFile>> upload(HttpServletRequest request) {
        MultipartHttpServletRequest multipartRequest;
        List<MultipartFile> files = null;

        if (request instanceof MultipartHttpServletRequest) {
            multipartRequest = (MultipartHttpServletRequest) (request);
            files = multipartRequest.getFiles("file");
        }

        List<PubFile> data = new ArrayList<>();
        AtomicInteger ok = new AtomicInteger();
        AtomicInteger failed = new AtomicInteger();
        String errMsg = null;

        if (files != null && !files.isEmpty()) {
            for (MultipartFile t : files) {
                RepBody<PubFile> pubFileRepBody = saveFile(t);
                if (pubFileRepBody.getCode() == RepCodeEnum.OK.getCode()) {
                    data.add(pubFileRepBody.getData());
                    ok.getAndIncrement();
                } else {
                    failed.getAndIncrement();
                    errMsg = "'" + t.getOriginalFilename() + "': " + pubFileRepBody.getMsg();
                }
            }

            RepBody<List<PubFile>> repBody = ok(data);
            if (StringUtils.isNotBlank(errMsg)) {
                repBody.setMsg(errMsg);
            } else {
                repBody.setMsg("成功：" + ok + ", 失败：" + failed);
            }

            return repBody;
        }

        return error(RepCodeEnum.ERR_UPLOAD_NULL);
    }

    /**
     * 保存
     *
     * @param file 附件
     * @return RepBody<PubFile>
     */
    private RepBody<PubFile> saveFile(MultipartFile file) {
        try {
            String sourceName = Objects.requireNonNull(file.getOriginalFilename());
            String pathRoot = getSavePathRoot();
            String date = DateHelper.getDate(new Date(), DateHelper.DATE_FORMAT_DATE);

            String suffix = FileTypeHelper.getFileType(file.getInputStream());
            if (StringUtils.isBlank(suffix)) {
                suffix = FileTypeHelper.getSuffix(sourceName);
            }

            if (!isAllow(suffix)) {
                return error(RepCodeEnum.ERR_UPLOAD_NOT_ALLOW);
            }

            String filename = RandomHelper.getFileName(FileTypeHelper.getSuffix(sourceName));

            File toFile = FileHelper.get(pathRoot + "//" + date, filename, true);
            BufferedOutputStream bos = FileHelper.getBOS(toFile);
            bos.write(file.getBytes());
            bos.flush();
            bos.close();

            PubFileForm pubFileForm = PubFileForm.builder()
                    .path(toFile.getPath())
                    .url(getFileServerBase() + "/" + date + "/" + filename)
                    .preUrl(getFileServerBase() + "/" + date + "/" + filename)
                    .userId(0)
                    .prePath("")
                    .sourceName(sourceName)
                    .size(file.getSize())
                    .type(suffix)
                    .typeName(suffix)
                    .build();
            PubFile pubFile = pubFileService.create(pubFileForm);

            return ok(pubFile);
        } catch (Exception e) {
            log.error("保存文件时发生错误：" + e.getMessage(), e);
        }

        return error(RepCodeEnum.UNKNOWN);
    }

    /**
     * 获取文件服务访问地址
     *
     * @return String
     */
    private String getFileServerBase() throws ClassNotFoundException,
            NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        List<CustomQueryParams> params = PubConfigHelper.newParams(CONFIG_TYPE_SYSTEM_NAME, CONFIG_KEY_FILE_SERVER_BASE_NAME);
        CustomQueryHelper<PubConfig> queryHelper = CustomQueryHelper.init(PubConfig.class, params, new ArrayList<>());

        RepPageData<PubConfig> data = pubConfigService.page(queryHelper.getPredicates(), queryHelper.getOrders(), false, 0, 0);

        List<PubConfig> list = data.getList();

        if (null == list || list.isEmpty()) {
            return defaultFileServerBase;
        }

        PubConfig pubConfig = list.get(0);

        return pubConfig.getCfgValue();
    }

    /**
     * 判断文件是否被允许上传
     *
     * @param suffix 文件后缀
     * @return boolean
     */
    private boolean isAllow(String suffix) throws ClassNotFoundException, NoSuchMethodException,
            InstantiationException, IllegalAccessException, InvocationTargetException {
        String allType = "*";
        Set<String> fileTypeAllow = getFileTypeAllow();

        final boolean[] res = {false};
        fileTypeAllow.forEach(t -> {
            if (t.equals(allType) || t.equals(suffix)) {
                res[0] = true;
            }
        });

        return res[0];
    }

    /**
     * 获取配置中允许上传的文件类型
     *
     * @return String[]
     */
    private Set<String> getFileTypeAllow() throws ClassNotFoundException, NoSuchMethodException,
            InvocationTargetException, InstantiationException, IllegalAccessException {
        List<CustomQueryParams> params = PubConfigHelper.newParams(CONFIG_TYPE_SYSTEM_NAME, CONFIG_KEY_ALLOW_FILE_TYPE_NAME);
        CustomQueryHelper<PubConfig> queryHelper = CustomQueryHelper.init(PubConfig.class, params, new ArrayList<>());

        RepPageData<PubConfig> data = pubConfigService.page(queryHelper.getPredicates(), queryHelper.getOrders(), false, 0, 0);

        List<PubConfig> list = data.getList();

        if (null == list || list.isEmpty()) {
            if(StringUtils.isNotBlank(defaultAllowFileType)){
                String[] allows = defaultAllowFileType.split(",");

                if("all".equals(defaultAllowFileType)){
                    allows[0] = "*";
                }

                return Arrays.stream(allows).collect(Collectors.toSet());
            }

            return new HashSet<>();
        }

        return list.stream()
                .filter(t -> StringUtils.isNotBlank(t.getCfgValue()))
                .map(PubConfig::getCfgValue)
                .collect(Collectors.toSet());
    }

    /**
     * 获取文件保存路径
     *
     * @return String
     */
    private String getSavePathRoot() throws ClassNotFoundException, NoSuchMethodException,
            InvocationTargetException, InstantiationException, IllegalAccessException {
        List<CustomQueryParams> params = PubConfigHelper.newParams(CONFIG_TYPE_SYSTEM_NAME, CONFIG_KEY_FILE_SAVE_PATH_NAME);
        CustomQueryHelper<PubConfig> queryHelper = CustomQueryHelper.init(PubConfig.class, params, new ArrayList<>());

        RepPageData<PubConfig> data = pubConfigService.page(queryHelper.getPredicates(), queryHelper.getOrders(), false, 0, 0);

        List<PubConfig> list = data.getList();

        if (null == list || list.isEmpty()) {
            return defaultSavePathRoot;
        }

        PubConfig pubConfig = list.get(0);

        return pubConfig.getCfgValue();
    }

}
